Search
Synchronizing Orders Across Shops in a WooCommerce Multisite Network with Global Cart - WP Global Cart
17572
documentation-template-default,single,single-documentation,postid-17572,theme-awake,eltd-core-1.1,woocommerce-no-js,awake child-child-ver-1.0.0,awake-ver-1.0,eltd-smooth-scroll,eltd-smooth-page-transitions,eltd-mimic-ajax,eltd-grid-1200,eltd-blog-installed,eltd-default-style,eltd-fade-push-text-top,eltd-header-standard,eltd-sticky-header-on-scroll-down-up,eltd-default-mobile-header,eltd-sticky-up-mobile-header,eltd-menu-item-first-level-bg-color,eltd-dropdown-slide-from-top,eltd-,eltd-fullscreen-search eltd-search-fade,eltd-side-menu-slide-from-right,wpb-js-composer js-comp-ver-6.3.0,vc_responsive
 

Synchronizing Orders Across Shops in a WooCommerce Multisite Network with Global Cart

WP Global Cart / Synchronizing Orders Across Shops in a WooCommerce Multisite Network with Global Cart
Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInShare on TumblrPin on PinterestEmail this to someonePrint this page

Synchronizing Orders Across Shops in a WooCommerce Multisite Network with Global Cart

WooCommerce Global Cart is an indispensable tool for managing multisite networks, enabling a unified shopping experience across multiple stores. Synchronizing orders across the network may be a required feature for streamlined management and optimal customer service. Here, we delve into how order synchronization can be achieved, exploring the default options, the Network Orders interface, and how advanced users can implement custom synchronization using the WordPress API.

Checkout Types and Default Order Creation

WooCommerce Global Cart offers two types of checkout processes:

  1. Single Checkout: In this mode, all products from different stores in the network are consolidated into a single checkout, creating a single order.
  2. Each Shop Checkout: In this approach, separate checkouts are processed for each store.

The default order creation happens during checkout, which provides a seamless experience for customers. This default behavior is designed to integrate smoothly into the multisite workflow, allowing store admins to easily manage orders where they originate. By aligning with WooCommerce’s native order-handling mechanisms, Global Cart ensures reliability and scalability.

Using the Network Orders Interface

Global Cart’s Network Orders interface aggregates all orders from the multisite network into one centralized dashboard. This feature is particularly useful for network administrators, who can view and manage all orders across stores in one place.

  • Super Admin Dashboard: By default, the Network Orders interface is accessible from the super admin area. This centralized view simplifies high-level order management and enhances visibility across the network.
  • Customizing the Display: The location of the Network Orders interface can be customized using the woogc/show_network_orders filter. This flexibility allows administrators to place the interface in a preferred location for specific workflows.

Programmatic Order Synchronization

In some cases, the default order creation and aggregation processes may not fully meet the unique requirements of a WooCommerce multisite network. For example, you might need to replicate orders across multiple stores for centralized reporting, inventory management, or operational alignment. This is where programmatic order synchronization comes into play, allowing developers to customize how orders are handled across the network.

What is Programmatic Synchronization?

Programmatic synchronization involves creating or updating orders in other shops within the multisite network through custom code. Using the WordPress API, developers can ensure that orders are accurately mirrored between the stores. This approach leverages WooCommerce’s extensive hooks, actions, and filters to maintain compatibility and consistency with core functionalities.

How Synchronization Works

The process of synchronization typically involves the following steps:

  1. Order Trigger: Synchronization is initiated by an event, such as the creation of a new order during checkout or a manual admin action. WooCommerce’s hooks, such as woocommerce_checkout_order_processed, can be used to detect these events.
  2. Data Extraction: The original order’s details, including items, quantities, billing, shipping, and payment information, are retrieved.
  3. Data Transfer: This data is then sent to the target store(s) using the WordPress API. The transfer may involve creating a new order in the target shop or updating an existing order to reflect changes.
  4. Order Creation in Target Store: The target store processes the data, ensuring it aligns with WooCommerce’s built-in order structure. All associated actions, such as inventory reduction or email notifications, are triggered automatically.
  5. Confirmation and Logging: After successful synchronization, the process can include logging for transparency and troubleshooting, ensuring administrators have visibility into the operation.

Implementation

Below is a practical example of code that demonstrates how to programmatically synchronize orders across a WooCommerce multisite network. The solution is divided into two essential components:

  • Client Code: This portion is responsible for gathering the relevant order data, preparing it for transmission, and sending it securely to the designated API endpoint.
  • Server Code: This component implements the API endpoint, receiving the data sent by the client, processing the request, and creating or updating the corresponding order on the target store.

The Client Code should be placed within a custom file on /wp-content/mu-plugins/ folder.
Inside, there are a few variables that require an update to match your actual environment:
– The $consumer_key and $consumer_secret should be updated with your own WooCommerce API Key
– The $WooGC_OrderSyncForBlogIDs should include the site ids which are allowed to synchronize the new orders, to the designated API.
– The $api_url is the actual site where the API will be available and where the orders will be synchronized.

    add_action('woocommerce_new_order', 'custom_order_created_action', 10, 1);
    function custom_order_created_action( $order_id )
        {
            // WooCommerce API credentials
            $consumer_key = 'ck_3b0643eb1c6e814eac68d026389fda9c738dbdb2';
            $consumer_secret = 'cs_6964e0d1f3226f240d98395ba8bd38bf0022d96c';
            
            $WooGC_OrderSyncForBlogIDs  =   array ( 1 );
            
            // WooCommerce API URL
            $api_url = 'http://other-domain.dev/wp-json/custom-api/v1/create-order';
            
            global $blog_id;
            
            if ( ! in_array ( $blog_id, $WooGC_OrderSyncForBlogIDs ) )
                return;
                        
            $order =   new WC_Order ( $order_id );
            
            $order_data                         =   array();
            $order_data["payment_method"]       =   $order->get_payment_method();
            $order_data["payment_method_title"] =   $order->get_payment_method_title();
            $order_data["set_paid"]             =   $order->is_paid();
            $order_data["billing"]              =   array (
                                                            "first_name" => $order->get_billing_first_name(),
                                                            "last_name"  => $order->get_billing_last_name(),
                                                            "address_1"  => $order->get_billing_address_1(),
                                                            "address_2"  => $order->get_billing_address_2(),
                                                            "city"       => $order->get_billing_city(),
                                                            "state"      => $order->get_billing_state(),
                                                            "postcode"   => $order->get_billing_postcode(),
                                                            "country"    => $order->get_billing_country(),
                                                            "email"      => $order->get_billing_email(),
                                                            "phone"      => $order->get_billing_phone()
                                                            );
            $order_data["shipping"]              =   array (
                                                            "first_name" => $order->get_shipping_first_name(),
                                                            "last_name"  => $order->get_shipping_last_name(),
                                                            "address_1"  => $order->get_shipping_address_1(),
                                                            "address_2"  => $order->get_shipping_address_2(),
                                                            "city"       => $order->get_shipping_city(),
                                                            "state"      => $order->get_shipping_state(),
                                                            "postcode"   => $order->get_shipping_postcode(),
                                                            "country"    => $order->get_shipping_country()
                                                            );
                                                            
            foreach ( $order->get_items()                   as  $order_item_id  =>  $order_item_data )
                {
                    $order_data["line_items"][]              =   array (
                                                                        "product_id"    => $order_item_data->get_product_id(),
                                                                        "variation_id"  => $order_item_data->get_variation_id(),
                                                                        "quantity"      => $order_item_data->get_quantity(),
                                                                        "subtotal"      => $order_item_data->get_subtotal(),
                                                                        "subtotal_tax"  => $order_item_data->get_subtotal_tax(),
                                                                        "total"         => $order_item_data->get_total(),
                                                                        "total_tax"     => $order_item_data->get_total_tax(),
                                                                        
                                                                        "blog_id"       => $order_item_data->get_meta( 'blog_id' ),
                                                                        );   
                }
                
            foreach ( $order->get_items( 'shipping' )                   as  $order_item_id  =>  $order_item_data )
                {
                    $order_data["shipping_lines"][]              =   array (
                                                                        "method_title"  =>  $order_item_data->get_name(),
                                                                        "total"         =>  $order_item_data->get_total()
                                                                        );   
                }
            
            // Combine the consumer key and secret
            $auth = base64_encode($consumer_key . ':' . $consumer_secret);

            // Set up the request headers
            $headers = array(
                'Authorization' => 'Basic ' . $auth,
                'Content-Type' => 'application/json'
            );

            // Prepare the data to send (empty object as example)
            $body = json_encode( $order_data );

            // Send the request
            $response = wp_remote_post($api_url, array(
                'method'    => 'POST',
                'body'      => $body,
                'headers'   => $headers,
                'timeout'   => 15,
            ));

            // Check for success or failure
            if (is_wp_error($response)) {
                error_log('Error: ' . $response->get_error_message());
            }
        }

The Server Code should be also placed within a custom file on /wp-content/mu-plugins/ folder. There are no requires on any variable change for this code:

 function custom_create_order_endpoint() 
            {
                register_rest_route('custom-api/v1', '/create-order', array(
                    'methods' => 'POST',
                    'callback' => 'custom_create_order',
                    'permission_callback' => '__return_true', // Allows request processing
                ));
            }
        add_action('rest_api_init', 'custom_create_order_endpoint');

        function custom_create_order( WP_REST_Request $request ) 
            { 
                // Get API credentials from the Authorization header
                $auth_header = $request->get_header('authorization');

                if (!$auth_header) {
                    return new WP_Error('no_auth', 'Authorization header is missing.', array('status' => 401));
                }

                // Decode the base64-encoded credentials
                if (strpos($auth_header, 'Basic ') === 0) {
                    $auth_header = substr($auth_header, 6); // Remove "Basic " prefix
                }

                $decoded_credentials = base64_decode($auth_header);
                list($consumer_key, $consumer_secret) = explode(':', $decoded_credentials);

                // Validate credentials
                $user_id = wc_api_check_credentials($consumer_key, $consumer_secret);

                if (!$user_id) {
                    return new WP_Error('invalid_credentials', 'Invalid API credentials.', array('status' => 403));
                }

                // Proceed to create the order if the credentials are valid
                $data = $request->get_json_params();
                
                if (empty($data)) {
                    return new WP_Error('no_data', 'No data provided', array('status' => 400));
                }

                $order = wc_create_order();

                if (isset($data['line_items'])) {
                    foreach ($data['line_items'] as $item ) 
                        {           
                            switch_to_blog( $item['blog_id'] );
                            $product    =   wc_get_product ( $item['variation_id'] ? $item['variation_id']   :   $item['product_id'] );
                            
                            if ( ! $product )
                                {
                                    restore_current_blog();
                                    continue;        
                                }
                            
                            restore_current_blog();
                            
                            $line_item = new WooGC_WC_Order_Item_Product();                            
                            $line_item->update_meta_data( 'blog_id', $item['blog_id'] );
                            
                            $line_item->set_product( $product );
                            $line_item->set_quantity( $item['quantity'] ? $item['quantity']   :   0 );
                            
                            $line_item->set_subtotal( $item['subtotal'] ? $item['subtotal']   :   0 );
                            $line_item->set_subtotal_tax( $item['subtotal_tax'] ? $item['subtotal_tax']   :   0 );
                            
                            $line_item->set_total( $item['total'] ? $item['total']   :   0 );
                            $line_item->set_total_tax( $item['total_tax'] ? $item['total_tax']   :   0 );
                            
                            $order->add_item ( $line_item );
                        }
                }
                
                if (isset($data['shipping_lines'])) {
                    foreach ($data['shipping_lines'] as $item ) 
                        {           
                            $shipping_item = new WC_Order_Item_Shipping();

                            $shipping_item->set_method_title( $item['method_title'] );
                            $shipping_item->set_total( $item['total'] );
                            $order->add_item( $shipping_item );
                        }
                }

                if (isset($data['billing'])) {
                    $order->set_address($data['billing'], 'billing');
                }
                if (isset($data['shipping'])) {
                    $order->set_address($data['shipping'], 'shipping');
                }

                if (isset($data['payment_method'])) {
                    $order->set_payment_method($data['payment_method']);
                }

                $order->calculate_totals();

                return [
                    'order_id' => $order->get_id(),
                    'status' => 'Order created successfully',
                ];
            } 

        /**
        * validate WooCommerce API credentials
        * 
        * @param mixed $consumer_key
        * @param mixed $consumer_secret
        */
        function wc_api_check_credentials( $consumer_key, $consumer_secret) 
            {
                global $wpdb;

                // Query for the consumer key and secret in the WooCommerce database table
                $query = $wpdb->prepare(
                    "SELECT user_id 
                     FROM {$wpdb->prefix}woocommerce_api_keys 
                     WHERE consumer_key = %s 
                       AND consumer_secret = %s 
                       AND permissions IN ('write', 'read_write')",
                    wc_api_hash ( $consumer_key ),
                    $consumer_secret
                );

                $user_id = $wpdb->get_var($query);

                // Return user ID if valid, or false otherwise
                return $user_id ?: false;
            }    

When to Use Order Synchronization

Order synchronization is not always necessary. In most cases, the default order creation and Network Orders interface provide sufficient functionality. However, programmatic synchronization becomes essential in specific use cases:

  • Centralized fulfillment workflows.
  • Complex reporting requirements where all orders must reside in a single database.
  • Custom integrations with third-party systems.

By implementing synchronization through the WordPress API, you gain full control over the process, ensuring that your multisite network meets its unique operational needs.

WooCommerce Global Cart provides flexible and robust options for managing orders in a multisite network. While the default features offer excellent functionality for most scenarios, advanced users can programmatically synchronize orders for added customization. By leveraging tools like the Network Orders interface and WordPress API, you can achieve unparalleled control over your multisite order management, ensuring smooth operations and a better customer experience.

0
Would love your thoughts, please comment.x
()
x